/*
 * Copyright (C) 2007 Eskil Bylund
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

using System;
using System.Collections.Generic;

using Gtk;
using Glade;
using Mono.Unix;

using DCSharp.Backend.Connections;

namespace DCSharp.Gui
{
	public class UserInformationDialog : GladeWindow
	{
		private static Dictionary<User, UserInformationDialog> dialogs =
			new Dictionary<User, UserInformationDialog>();

		private User user;
		private ListStore hubStore;
		private MessageArea offlineMessage;

		[Widget]
		private Label userLabel;

		[Widget]
		private ComboBox hubComboBox;

		[Widget]
		private Box infoBox;

		[Widget]
		private Label nickTextLabel;

		[Widget]
		private Label nickLabel;

		[Widget]
		private Label emailLabel;

		[Widget]
		private Label descriptionLabel;

		[Widget]
		private Label connectionLabel;

		[Widget]
		private Label sharedLabel;

		[Widget]
		private Label clientLabel;

		[Widget]
		private Button closeButton;

		public UserInformationDialog(User user) : this(user, null)
		{
		}

		public UserInformationDialog(User user, Window parent) :
			base("UserInformationDialog.glade", parent)
		{
			if (user == null)
			{
				throw new ArgumentNullException("user");
			}
			this.user = user;

			// General
			userLabel.Markup = String.Format("<b>{0}</b>", GLib.Markup.EscapeText(user.Nick));
			hubComboBox.Changed += OnHubChanged;

			// Offline message
			offlineMessage = new MessageArea(
				Catalog.GetString("The user is now offline on this hub"), null,
				MessageType.Info);

			infoBox.Show();
			infoBox.PackStart(offlineMessage, false, false, 0);

			// Model
			hubStore = new ListStore(typeof(HubConnection), typeof(Identity));
			hubComboBox.Model = hubStore;

			// View
			CellRendererPixbuf pixbufRenderer = new CellRendererPixbuf();
			CellRendererText textRenderer = new CellRendererText();

			textRenderer.Xpad = 3;

			hubComboBox.PackStart(pixbufRenderer, false);
			hubComboBox.PackStart(textRenderer, true);
			hubComboBox.SetCellDataFunc(pixbufRenderer, SetPixbuf);
			hubComboBox.SetCellDataFunc(textRenderer, SetText);

			// Initialize
			user.EnumerateOnlineHubs(delegate(HubConnection hub)
			{
				hubStore.AppendValues(hub, hub.Users[user]);
			});
			TrySetFirstActive();

			// Events
			closeButton.Clicked += OnCloseButtonClicked;

			user.OnlineOnHub += OnHubsChanged;
			user.OfflineOnHub += OnHubsChanged;
		}

		#region Properties

		public User User
		{
			get { return user; }
		}

		#endregion

		#region Methods

		public static UserInformationDialog Create(User user, Window parent)
		{
			UserInformationDialog dialog;
			if (!dialogs.TryGetValue(user, out dialog))
			{
				dialog = new UserInformationDialog(user, parent);
				dialog.Window.Destroyed += delegate
				{
					dialogs.Remove(user);
				};
				dialogs.Add(user, dialog);
			}
			return dialog;
		}

		public override void Destroy ()
		{
			user.OnlineOnHub -= OnHubsChanged;
			user.OfflineOnHub -= OnHubsChanged;

			hubStore.Clear();

			base.Destroy();
		}

		private void UpdateHubStore(HubConnection toggledHub)
		{
			// Update the hub if it's in the store, otherwise add it.
			TreeIter iter;
			if (GetHubIter(toggledHub, null, out iter))
			{
				// It's a hub already in the store. Update the identity if
				// the user's online on the hub.
				Identity identity = toggledHub.Users[user];
				if (identity != null)
				{
					hubStore.SetValue(iter, 1, identity);
				}
				UpdateInfoIfActive(iter);
			}
			else
			{
				// It's a hub not in the store.
				if (GetHubIter(null, toggledHub.Uri, out iter))
				{
					HubConnection hub = (HubConnection)hubStore.GetValue(iter, 0);
					if (hub != null && hub.State == ConnectionState.Disconnected)
					{
						// Replace an old disconnected connection to this hub
						// with the new connection.
						hubStore.SetValue(iter, 0, toggledHub);
						hubStore.SetValue(iter, 1, toggledHub.Users[user]);

						UpdateInfoIfActive(iter);
						return;
					}
				}

				// Add the new hub.
				hubStore.AppendValues(toggledHub, toggledHub.Users[user]);
				TrySetFirstActive();
			}
		}

		private void UpdateInfoIfActive(TreeIter iter)
		{
			TreeIter activeIter;
			if (hubComboBox.GetActiveIter(out activeIter) &&
				activeIter.Equals(iter))
			{
				OnHubChanged(null, null);
			}
		}

		private void TrySetFirstActive()
		{
			if (hubComboBox.Active < 0 && hubStore.IterNChildren() > 0)
			{
				hubComboBox.Active = 0;
			}
		}

		private bool GetHubIter(HubConnection hub, Uri uri, out TreeIter iter)
		{
			if (hubStore.GetIterFirst(out iter))
			{
				do
				{
					HubConnection h = (HubConnection)hubStore.GetValue(iter, 0);
					if (h != null &&
						((hub != null && h == hub) || (uri != null && h.Uri == uri)))
					{
						return true;
					}
				}
				while (hubStore.IterNext(ref iter));
			}
			return false;
		}

		private void SetPixbuf(CellLayout cellLayout, CellRenderer renderer,
			TreeModel model, TreeIter iter)
		{
			CellRendererPixbuf pixbufRenderer = (CellRendererPixbuf)renderer;

			HubConnection hub = (HubConnection)model.GetValue(iter, 0);
			pixbufRenderer.Pixbuf = hub != null ? Util.GetHubIcon(hub) : null;
		}

		private void SetText(CellLayout cellLayout, CellRenderer renderer,
			TreeModel model, TreeIter iter)
		{
			CellRendererText textRenderer = (CellRendererText)renderer;

			HubConnection hub = (HubConnection)model.GetValue(iter, 0);
			if (hub != null)
			{
				textRenderer.Text = Util.GetHubName(hub);				
			}
		}

		private void OnCloseButtonClicked(object obj, EventArgs args)
		{
			Destroy();
		}

		private void OnHubChanged(object obj, EventArgs args)
		{
			TreeIter iter;
			if (hubComboBox.GetActiveIter(out iter))
			{
				Identity identity = (Identity)hubStore.GetValue(iter, 1);
				HubConnection hub = (HubConnection)hubStore.GetValue(iter, 0);

				if (hub != null && identity != null)
				{
					bool onlineOnHub = hub.Users.Contains(identity);

					offlineMessage.Visible = !onlineOnHub;

					nickLabel.Text = identity.Nick;
					nickTextLabel.Visible = nickLabel.Visible =
						identity.Nick != user.Nick;

					string unknown = "-";

					emailLabel.Text = identity.Email ?? unknown;
					descriptionLabel.Text = identity.Description ?? unknown;
					sharedLabel.Text = identity.ShareSize > 0 ?
						Util.FormatFileSize(identity.ShareSize) : unknown;
					clientLabel.Text = identity.Client ?? unknown;

					if (identity.Connection != null)
					{
						// FIXME: There's probably a better way to handle different decimal separators.
						string connection = identity.Connection.Replace(",",
							System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator);
						connection = connection.Replace(".",
							System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator);

						float result;
						if (float.TryParse(connection, out result))
						{
							connectionLabel.Text = String.Format("{0} Mbit/s", result);
						}
						else
						{
							connectionLabel.Text = identity.Connection ?? unknown;
						}
					}
					else
					{
						connectionLabel.Text = unknown;
					}
				}
			}
		}

		private void OnHubsChanged(object obj, ConnectionEventArgs args)
		{
			Application.Invoke(delegate
			{
				UpdateHubStore((HubConnection)args.Connection);
				Window.QueueDraw();
			});
		}

		#endregion
	}
}
